#include "Empire.h"
#include "EmpireStrings.h"
#include "Output.h"
#include "Global.h"
#include "BbsControlData.h"
#include "Input.h"
#include "Users.h"

#include <stdio.h>
#include <unistd.h>
#include <device.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>

#pragma code-name("EMPIRECCODE");
#pragma rodata-name("EMPIRECCODE");
#pragma data-name("EMPIRECDATA");
#pragma bss-name("EMPIRECBSS");

bool loadPlayer(empirePlayer_t *player, int playerNumber);
bool savePlayer(empirePlayer_t *player);

char buffer[81];

long calculateMight(empirePlayer_t *player)
{
	long result = 
		player->Infantry + 
		player->Archers +
		player->Cavalry * 2L +
		player->Catapults * 10L +
		player->Knights * 5L ;	

	return result;
}

void catapultAttack(empirePlayer_t *attacker, empirePlayer_t *defender)
{
	static long temp, killed;
	static char units[11];

	if(attacker->Catapults > 0)
	{
		temp = rand() % 5 * attacker->Catapults;
		if(temp == 0) temp = 3;
		if(defender->Catapults > 0)
		{
			killed = (temp > defender->Catapults/5 ? defender->Catapults/5 : temp);
			defender->Catapults = (defender->Catapults * 5 - temp) / 5;	// Catapults remaining;
			if(defender->Catapults < 0) defender->Catapults = 0;
			strcpy(units, "Catapults");
		}
		else if(defender->Knights > 0)
		{
			killed = (temp > defender->Knights/15 ? defender->Knights/15 : temp);
			defender->Knights = (defender->Knights * 15 - temp) / 15; // knights remaining;
			if(defender->Knights < 0) defender->Knights = 0;
			strcpy(units, "Knights");
		}
		else if(defender->Cavalry > 0)
		{
			killed = (temp > defender->Cavalry/2 ? defender->Cavalry/2 : temp);
			defender->Cavalry = (defender->Cavalry * 2 - temp) / 2;
			if(defender->Cavalry < 0) defender->Cavalry = 0;
			strcpy(units, "Cavalry");
		}
		else if(defender->Archers > 0)
		{
			killed = (temp > defender->Archers ? defender->Archers : temp);
			defender->Archers = (defender->Archers - temp);
			if(defender->Archers < 0) defender->Archers = 0;
			strcpy(units, "Archers");
		}
		else 
		{
			killed = (temp > defender->Infantry ? defender->Infantry : temp);
			defender->Infantry = (defender->Infantry - temp);
			if(defender->Infantry < 0) defender->Infantry = 0;
			strcpy(units, "Infantry");
		}

		sprintf(buffer, EMPIRE_CONQUEST_CAT_AT, attacker->kingdomName, killed, units);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
	}
}
void knightAttack(empirePlayer_t *attacker, empirePlayer_t *defender)
{
	static long temp, killed;
	static char units[11];

	if(attacker->Knights > 0)
	{
		temp = rand() % 15 * (attacker->Knights);
		if(temp == 0) temp = 7;
		if(defender->Catapults > 0)
		{
			killed = (temp > defender->Catapults/5 ? defender->Catapults/5 : temp);
			defender->Catapults = (defender->Catapults * 5 - temp) / 5;	// Catapults remaining;
			if(defender->Catapults < 0) defender->Catapults = 0;
			strcpy(units, "Catapults");
		}
		else if(defender->Knights > 0)
		{
			killed = (temp > defender->Knights/15 ? defender->Knights/15 : temp);
			defender->Knights = (defender->Knights * 15 - temp) / 15; // knights remaining;
			if(defender->Knights < 0) defender->Knights = 0;
			strcpy(units, "Knights");
		}
		else if(defender->Cavalry > 0)
		{
			killed = (temp > defender->Cavalry/2 ? defender->Cavalry/2 : temp);
			defender->Cavalry = (defender->Cavalry * 2 - temp) / 2;
			if(defender->Cavalry < 0) defender->Cavalry = 0;
			strcpy(units, "Cavalry");
		}
		else if(defender->Archers > 0)
		{
			killed = (temp > defender->Archers ? defender->Archers : temp);
			defender->Archers = (defender->Archers - temp);
			if(defender->Archers < 0) defender->Archers = 0;
			strcpy(units, "Archers");
		}
		else 
		{
			killed = (temp > defender->Infantry ? defender->Infantry : temp);
			defender->Infantry = (defender->Infantry - temp);
			if(defender->Infantry < 0) defender->Infantry = 0;
			strcpy(units, "Infantry");
		}

		sprintf(buffer, EMPIRE_CONQUEST_KNI_AT, attacker->kingdomName, killed, units);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
	}
}
void cavalryAttack(empirePlayer_t *attacker, empirePlayer_t *defender)
{
	static long temp, killed;
	static char units[11];

	if(attacker->Cavalry > 0)
	{
		temp = rand() % 3 * (attacker->Cavalry);
		if(temp == 0) temp = 2;
		if(defender->Infantry > 0)
		{
			temp *= 2;
			killed = (temp > defender->Infantry ? defender->Infantry : temp);
			defender->Infantry = (defender->Infantry - temp);
			if(defender->Infantry < 0) defender->Infantry = 0;
			strcpy(units, "Infantry");
		}
		else if(defender->Cavalry > 0)
		{
			killed = (temp > defender->Cavalry/2 ? defender->Cavalry/2 : temp);
			defender->Cavalry = (defender->Cavalry * 2 - temp) / 2;
			if(defender->Cavalry < 0) defender->Cavalry = 0;
			strcpy(units, "Cavalry");
		}
		else if(defender->Archers > 0)
		{
			temp /= 2;
			killed = (temp > defender->Archers ? defender->Archers : temp);
			defender->Archers = (defender->Archers - temp);
			if(defender->Archers < 0) defender->Archers = 0;
			strcpy(units, "Archers");
		}

		sprintf(buffer, EMPIRE_CONQUEST_CAV_AT, attacker->kingdomName, killed, units);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
	}
}
void archerAttack(empirePlayer_t *attacker, empirePlayer_t *defender)
{
	static long temp, killed;
	static char units[11];

	if(attacker->Archers > 0)
	{
		temp = attacker->Archers;		
		if(defender->Cavalry > 0)
		{
			temp *= 2;
			killed = (temp > defender->Cavalry/2 ? defender->Cavalry/2 : temp);
			defender->Cavalry = (defender->Cavalry * 2 - temp) / 2;
			if(defender->Cavalry < 0) defender->Cavalry = 0;
			strcpy(units, "Cavalry");
		}
		else if(defender->Archers > 0)
		{
			killed = (temp > defender->Archers ? defender->Archers : temp);
			defender->Archers = (defender->Archers - temp);
			if(defender->Archers < 0) defender->Archers = 0;
			strcpy(units, "Archers");
		}
		else if(defender->Infantry > 0)
		{
			temp /= 2;
			killed = (temp > defender->Infantry ? defender->Infantry : temp);
			defender->Infantry = (defender->Infantry - temp);
			if(defender->Infantry < 0) defender->Infantry = 0;
			strcpy(units, "Infantry");
		}

		sprintf(buffer, EMPIRE_CONQUEST_ARC_AT, attacker->kingdomName, killed, units);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
	}
}
void infantryAttack(empirePlayer_t *attacker, empirePlayer_t *defender)
{
	static long temp, killed;
	static char units[11];

	if(attacker->Infantry > 0)
	{
		temp = attacker->Infantry;		
		if(defender->Archers > 0)
		{
			temp *= 2;
			killed = (temp > defender->Archers ? defender->Archers : temp);
			defender->Archers = (defender->Archers - temp);
			if(defender->Archers < 0) defender->Archers = 0;
			strcpy(units, "Archers");
		}
		else if(defender->Infantry > 0)
		{
			killed = (temp > defender->Infantry ? defender->Infantry : temp);
			defender->Infantry = (defender->Infantry - temp);
			if(defender->Infantry < 0) defender->Infantry = 0;
			strcpy(units, "Infantry");
		}
		else if(defender->Cavalry > 0)
		{
			temp /= 2;
			killed = (temp > defender->Cavalry/2 ? defender->Cavalry/2 : temp);
			defender->Cavalry = (defender->Cavalry * 2 - temp) / 2;
			if(defender->Cavalry < 0) defender->Cavalry = 0;
			strcpy(units, "Cavalry");
		}

		sprintf(buffer, EMPIRE_CONQUEST_INF_AT, attacker->kingdomName, killed, units);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
	}
}

void conquest(void)
{
	static char i, key;
	static empirePlayer_t opponent;
	static int opponentSelected;
	static long acres, gold;

	if(_empireSettings.numberOfPlayers == 1)
	{
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_NONE);
		return;
	}

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_PICK_1);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_PICK_2);

	for(i = 0; i < _empireSettings.numberOfPlayers; ++i)
	{
		if(i != _currentEmpirePlayer.playerNumber)
		{
			sprintf(buffer, EMPIRE_CONQUEST_PICK_T, i+1, _empireSettings.players[i].kingdonName,
				_empireSettings.players[i].might, _empireSettings.players[i].acres);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
		}
	}

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_PICK_3);
	sprintf(buffer, EMPIRE_CONQUEST_PICK_4, _empireSettings.players[_currentEmpirePlayer.playerNumber].might);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
	sprintf(buffer, EMPIRE_CONQUEST_PICK_5, _empireSettings.numberOfPlayers - 1);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);

	opponentSelected = -1;

	while(opponentSelected < 0 
		|| opponentSelected > _empireSettings.numberOfPlayers - 1
		|| opponentSelected == _currentEmpirePlayer.playerNumber + 1)
	{
		input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_NUMERIC, buffer, 2, false);
		opponentSelected = atoi(buffer);
		if(_currentEmpirePlayer.attackedToday[opponentSelected - 1])
		{
			opponentSelected = -1;
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_ALREADY);
		}

		if(opponentSelected < 0 
		|| opponentSelected > _empireSettings.numberOfPlayers - 1
		|| opponentSelected == _currentEmpirePlayer.playerNumber + 1)
		{
			sprintf(buffer, EMPIRE_CONQUEST_PICK_5, _empireSettings.numberOfPlayers - 1);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
		}
	}
	
	if(opponentSelected == 0) 
	{
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_NO);
		return;
	}

	--opponentSelected;

	loadPlayer(&opponent, opponentSelected);

	srand(_systime());
			
	while(_currentEmpirePlayer.Infantry +
		_currentEmpirePlayer.Archers +
		_currentEmpirePlayer.Cavalry > 0
		&& 
		opponent.Infantry +
		opponent.Archers +
		opponent.Cavalry > 0)
	{
		catapultAttack(&_currentEmpirePlayer, &opponent);
		catapultAttack(&opponent, &_currentEmpirePlayer);
		
		knightAttack(&_currentEmpirePlayer, &opponent);
		cavalryAttack(&_currentEmpirePlayer, &opponent);
		archerAttack(&_currentEmpirePlayer, &opponent);
		infantryAttack(&_currentEmpirePlayer, &opponent);

		knightAttack(&opponent, &_currentEmpirePlayer);
		cavalryAttack(&opponent, &_currentEmpirePlayer);
		archerAttack(&opponent, &_currentEmpirePlayer);
		infantryAttack(&opponent, &_currentEmpirePlayer);
	}

	if(opponent.Infantry +
		opponent.Archers +
		opponent.Cavalry == 0)
	{
		acres = opponent.acres > 2000 ? opponent.acres / 10 : 200;
		gold = opponent.gold / 2;

		if(opponent.acres < acres)
		{
			_currentEmpirePlayer.acres += opponent.acres;
			_currentEmpirePlayer.peasants += opponent.peasants;
			_currentEmpirePlayer.gold += opponent.gold;
			_currentEmpirePlayer.food += opponent.food;
			_currentEmpirePlayer.lumber += opponent.lumber;
			_currentEmpirePlayer.stone += opponent.stone;

			opponent.gold = _empireSettings.startingGold;
			opponent.acres = _empireSettings.startingAcres;
			opponent.lumber = _empireSettings.startingLumber;
			opponent.food = 0;
			opponent.stone = _empireSettings.startingStone;
			opponent.peasants = _empireSettings.startingPeasants;

			opponent.Infantry = _empireSettings.startingInfantry;
			opponent.Archers = _empireSettings.startingArchers;
			opponent.Cavalry = _empireSettings.startingCavalry;
			opponent.Catapults = _empireSettings.startingCatapults;
			opponent.Knights = _empireSettings.startingKnights;
			opponent.Nobles = _empireSettings.startingNobles;

			opponent.lumbermills = 5;
			opponent.mines = 5;
			opponent.markets = 0;
			opponent.foundries = 0;
			opponent.brothels = 0;
			opponent.pubs = 0;
			opponent.banks = 0;

			opponent.wipedOutOn = _systime() + _baseTime;
			strcpy(opponent.wipedOutBy, _currentEmpirePlayer.kingdomName);

			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_WON_1);
			sprintf(buffer, EMPIRE_CONQUEST_WON_2, calculateMight(&_currentEmpirePlayer));
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_WIPOUT);
		}
		else
		{
			opponent.acres -= acres;
			opponent.gold -= gold;

			_currentEmpirePlayer.acres += acres;
			_currentEmpirePlayer.gold += gold;

			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_WON_1);
			sprintf(buffer, EMPIRE_CONQUEST_WON_2, calculateMight(&_currentEmpirePlayer));
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
			sprintf(buffer, EMPIRE_CONQUEST_WON_3, acres);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
			sprintf(buffer, EMPIRE_CONQUEST_WON_4, gold);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
		}
	}
	else
	{
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_CONQUEST_LOST);
	}

	_currentEmpirePlayer.attackedToday[opponentSelected] = true;

	savePlayer(&opponent);
	savePlayer(&_currentEmpirePlayer);

	return;
}

bool loadPlayer(empirePlayer_t *player, int playerNumber)
{
	static bool result;
	static FILE *file;
	static char oldDirectory[17];
	static char directory[17];
	static char filename[21];
	static char time1[13];
	static char time2[13];
	static struct tm *temp;
	static long yday1, yday2;
	static long year1, year2;
	static time_t currentTime;

	result = false;

	getcwd(oldDirectory, 17);
	getdevicedir(bcd.dataDirectory.device, directory, 17);
	chdir(directory);

	sprintf(filename, "emp-plr-%04X", playerNumber);

	file = fopen(filename, "rb");

	if(file != NULL)
	{
		result = (bool)fread(player, sizeof(empirePlayer_t), 1, file);
	}

	fclose(file);

	chdir(oldDirectory);

	if(result)
	{
		if(player == &_currentEmpirePlayer)
		{
			temp = localtime(&(_currentEmpirePlayer.lastPlayed));
			yday1 = temp->tm_yday;
			year1 = temp->tm_year;

			currentTime = (_systime() + _baseTime);
			temp = localtime(&currentTime);
			yday2 = temp->tm_yday;
			year2 = temp->tm_year;

			if(yday1 != yday2 && year1 != year2)
			{
				sprintf(buffer, "%u - %u", yday1, year1);
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);

				sprintf(buffer, "%u - %u", yday2, year2);
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
				
				player->lastPlayed = _systime() + _baseTime;
				player->turnsRemaining = _empireSettings.turnsPerDay;
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_NEW_DAY);
			}
		}
	}
	else
	{
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF CHX_RED "Could not load player record.");
	}
	return result;
}

bool savePlayer(empirePlayer_t *player)
{
	static bool result;
	static FILE *file;
	static char oldDirectory[17];
	static char directory[17];
	static char filename[21];

	getcwd(oldDirectory, 17);
	getdevicedir(bcd.dataDirectory.device, directory, 17);
	chdir(directory);

	sprintf(filename, "emp-plr-%04X", player->playerNumber);

	file = fopen(filename, "wb");

	if(file != NULL)
	{
		result = (bool)fwrite(player, sizeof(empirePlayer_t), 1, file);
	}
	else
	{
		result = false;
	}

	fclose(file);

	chdir(oldDirectory);

	if(player == &_currentEmpirePlayer)
	{
		if(result) outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), EMPIRE_PLAYER_SAVED);
		else 
		{
			sprintf(buffer, "Error saving player: %u", _oserror);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
		}
	}

	_empireSettings.players[player->playerNumber].acres = player->acres;
	_empireSettings.players[player->playerNumber].gold = player->gold;
	_empireSettings.players[player->playerNumber].might = calculateMight(player);

	return result;
}
